/**
 * 模板方法,将模板字符串转化成模板函数,或将模板字符串和模板变量转化成输出字符串.
 * 
 * 模板语法
 * %{JS_CODE} js代码的直接量输出,中间js code的运算结果会包含在输出字符串中
 * {%JS_CODE%} js运行逻辑代码,不会输出.
 * 
 * !!注意事项: 暂时不支持在模板中非JS代码片段中使用单引号
 * 
 * @usage
 * <xmp id="t1" style="display:none">
 * <ul>
 *  {% for (var i=0,len=data.length; i<len; ++i){ %}
 *      <li class="%{i % 2 == 0 ? 'even' : 'odd'}">
 *          <a href="%{data[i].url}">data[i].desc</a>
 *      </li>
 *  {% } %}
 * </ul>
 * </xmp>
 * <script>
 * var template = baidu.g('t1').innerHTML,
 *  mockData = {data:[{url:"template.js",desc:"me"},{url:"no.css",desc:"No"}]};
 * 
 * //获取模板结果
 * //方法1:
 * var targetString = baidu.string.template(template,mockData);
 * //方法2:
 * var templateFn = baidu.string.template(template);
 * var targetString = templateFn(mockData);
 * </script>
 * 
 * 
 * @seealso http://ejohn.org/blog/javascript-micro-templating/
 */
//var testData = [3,6,0];
//var testStr = '<ul>@! for (var i = d.length - 1; i >= 0; i--) { @<li>!@d[i]@</li>@! }; @</ul>';
//var testStr2 = '<ul>@@each d as _v@<li>!@_v@</li>@@/each@</ul>';


var template = function(str, data) {
    var self = arguments.callee,
        cache = self._cache = self._cache || {},
        conf = self._conf = self._conf || {
            'synxJS': {
                begin: '@!',
                end: '@'
            },
            'synxTPL': {
                begin: '@@',
                end: '@'
            },
            'synxVAR': {
                begin: '!@',
                end: '@'
            }
        },
        TPL_B = conf.synxTPL.begin,
        TPL_E = conf.synxTPL.end;
    self.synxBegin = {
        'each': function(arg) {
            var argName = arg.match(/^\s*(\S*)/)[1],
                asName = arg.match(/as\s*(\S*)/i)[1];//'$value';//
            return '$.each(' + argName + ', function($index,' + asName + '){';
        },
        'if': 'if ($1) {',
        'else': '} else {'
    };
    self.synxEnd = {
        'each': '});',
        'if': '};'
    };

    var fnBody = "var p=[];\
            with(obj){\
                p.push('" +
    //TODO: 暂不支持模板中非语法部分使用单引号
    // 汇成一行
    str.replace(/[\r\t\n]/g, " ") 
    //直接量输出,支持javascript语法输出 // /\%\{(.+?)\}/g
    .replace(new RegExp( conf.synxVAR.begin + '(.+?)' + conf.synxVAR.end, 'g'), "',$1,'")
    //将模板起始语法切换成js语法 // /\{\{([^\/][^\s\}]*?)(.*?)\}\}/ig,
    .replace(new RegExp( TPL_B + '([^\\/][^\\s' + TPL_E +']*)(.*?)' + TPL_E,'ig'), function($0, $1, $2) {
        var ret = self.synxBegin[$1];
        return conf.synxJS.begin + (typeof ret == 'function' ? ret($2) : ret.replace('$1', $2)) + conf.synxJS.end;
    })
    //将模板结束语法切换成js语法 // /\{\{\/(\S*?)\}\}/ig
    .replace(new RegExp( TPL_B + '\\/(\\S*?)' + TPL_E ,'ig'), function($0, $1) {
        return conf.synxJS.begin + self.synxEnd[$1] + conf.synxJS.end;
    })
    //将js语法块独立出来 // /\{%(.+?)%\}/g
    .replace(new RegExp( conf.synxJS.begin + '(.+?)' + conf.synxJS.end ,'g'), "'); $1 p.push('") 
        + "');\
        }\
        return p.join('');";
    //缓存生成的function,而非单次模板结果.
    var fn = cache[str] = cache[str] || new Function("obj", fnBody);
    // 若未传入数据,则仅返回模板方法,用于后续调用
    return (data ? fn(data) : fn);
}

//alert($.fn.tpl(testStr2, {d:testData}));
/*
function template(str, data) {
    var self = arguments.callee, 
        cache = self._cache = self._cache || {}, 
        //缓存生成的function,而非单次模板结果.
        fn = cache[str] = cache[str]    
            || 
            new Function("obj", 
                "var p=[];\
                with(obj){\
                    p.push('"
                    +
                    //TODO: 暂不支持模板中非语法部分使用单引号
                    // 汇成一行
                    str.replace(/[\r\t\n]/g, " ")
                        //直接量输出,支持javascript语法输出
                        .replace(/\%\{(.+?)\}/g, "',$1,'")
                        //将js语法块独立出来
                        .replace(/\{%(.+?)%\}/g,"'); $1 p.push('")
                    + "');\
                }\
                return p.join('');");
    // 若未传入数据,则仅返回模板方法,用于后续调用
    return data ? fn(data) : fn;
};
*/
